// 1.构造函数继承
// 在子类的构造函数中执行父类的构造函数。并为其绑定子类的this，让父类的构造函数把成员的属性和方法都挂在子类的this这样能避免实例之间共享一个原型实例，又能向父类构造函数传参
// 缺点：继承不到父类原型上的属性和方法
// ---
// 父类
function Parent1 () {
    this.name = 'Klein'
}

// 父类的原型方法
Parent1.prototype.getName = function () {
    return this.name
}

// 子类
function Child1 () {
    Parent1.call(this)
    this.type = 'child'
}

const child1 = new Child1()
console.log(child1) // Child1 { name: 'Klein', type: 'child' }
// console.log(child1.getName()) // TypeError: child1.getName is not a function

// 2.原型链继承
// 让子类的原型指向父类的实例，当子类实例找不到对用的属性和方法时，就会沿着原型链向上找，也就是去父类的实例上找，从而实现对父类属性和方法的继承。
// 缺点：两个实例引用的是同一个原型对象。
// ---
// 父类
function Parent2 () {
    this.name = 'Klein'
    this.play = [1, 2, 3]
}

// 父类的原型方法
Parent2.prototype.getName = function () {
    return this.name
}

// 子类
function Child2 () {
    this.type = 'child'
}

// 子类的原型对象指向父类实例
Child2.prototype = new Parent2()

const child2 = new Child2()
console.log(child2) // Parent2 { type: 'child' }
console.log(child2.getName()) // Klein

const child01 = new Child2()
const child02 = new Child2()
child01.play.push(4)
console.log(child01.play, child02.play) // [ 1, 2, 3, 4 ] [ 1, 2, 3, 4 ]

// 3.组合式继承
// 原型链继承和构造函数继承各有互补的优缺点 综合二者
// 缺点：子类创建实例时，原型中会存在两份相同的属性和方
// ---
// 父类
function Parent3 () {
    this.name = 'klein'
    this.play = [1, 2, 3]
}

// 父类的原型方法
Parent3.prototype.getName = function () {
    return this.name
}

// 子类
function Child3 () {
    // 构造函数继承
    Parent3.call(this)
    this.type = 'child'
}

//原型链继承
Child3.prototype = new Parent3()

const child001 = new Child3()
const child002 = new Child3()
console.log(child001) // Child3 { name: 'klein', play: [ 1, 2, 3 ], type: 'child' }
console.log(child001.getName()) // klein
child001.play.push(4)
console.log(child001.play, child002.play) // [ 1, 2, 3, 4 ] [ 1, 2, 3 ]

// 4.寄生式组合继承
// 为了解决构造函数被执行两次的问题, 我们将指向父类实例改为指向父类原型, 减去一次构造函数的执行。
// ---
// 父类
function Parent4 () {
    this.name = 'klein'
    this.play = [1, 2, 3]
}

// 父类的原型方法
Parent4.prototype.getName = function () {
    return this.name
}

// 子类
function Child4 () {
    Parent4.call(this)
    this.type = 'child'
}

// 将`指向父类实例`改为`指向父类原型`
// 给Parent4.prototype做一个浅拷贝
Child4.prototype = Object.create(Parent4.prototype)
Child4.prototype.constructor = Child4

const child0001 = new Child4()
const child0002 = new Child4()
console.log(child0001) // Child4 { name: 'klein', play: [ 1, 2, 3 ], type: 'child' }
console.log(child0001.getName()) // klein
child0001.play.push(4)
console.log(child0001.play, child0002.play) // [ 1, 2, 3, 4 ] [ 1, 2, 3 ]
